/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: client_requester.cpp
 *
 * Purpose: client endian base class implementation for 'COM' Functional.
 *
 * Developer:
 *   wen.gu , 2020-06-15
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#define LOG_TAG "CLIR"

#include "panda/com/client_requester.h"

#include <memory>
#include <thread>

#include "panda/sys/message.h"
#include "panda/core/function_executor.h"
#include "panda/sys/ipc_client_requester.h"
#include "panda/core/utils.h"


#include "panda/core/log.h"

namespace panda
{
namespace com
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/
#define MakeIpcClientRequesterPtr() std::make_shared<IpcClientRequester>()
/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/
using PandaErrc = panda::core::PandaErrc;
using Value = panda::core::Value;
using IpcClientRequester = panda::sys::IpcClientRequester;
using IpcPacketData = panda::sys::IpcPacketData;

using IpcClientRequesterPtr = std::shared_ptr<IpcClientRequester>;

class ClientRequester::impl
{

public:
    IpcClientRequesterPtr mClient;
};
/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
static PandaErrc MyClientRequest(IpcClientRequester* client, const std::string& method, const Value& param, Value& reply, int32_t waitMs = COM_REQ_WAIT_INFINITE)
{
    IpcPacketData rpy;
    std::string paramStr;
    if (panda::core::JsonWriteRaw(param, paramStr))
    {
        PandaErrc ret = client->request(method, (const uint8_t*)paramStr.data(), paramStr.size(), rpy, waitMs);

        if (PandaErrc::OK == ret)
        {
            if (rpy.payload && (rpy.size > 0))
            {
                if (panda::core::JsonParse((const char*)rpy.payload.get(), (const char*)rpy.payload.get() + rpy.size, reply))
                {
                    return PandaErrc::OK;
                }
                else
                {
                    ret = PandaErrc::Undefined;
                }
            }
            else
            {
                ret = PandaErrc::Undefined;
            }
        }

        return ret;
    }
    
    return PandaErrc::BadParameter;
}

static PandaErrc MyClientRequestAsync(IpcClientRequester* client, const std::string& method, const Value& param, ClientReplyHandler handler)
{
    if (method.empty() || !handler) return PandaErrc::BadParameter;
    IpcPacketData rpy;
    std::string paramStr;
    if (panda::core::JsonWriteRaw(param, paramStr))
    {
        PandaErrc ret = client->requestAsync(method, (const uint8_t*)paramStr.data(), paramStr.size(), [handler](const std::string& name, const IpcPacketData& data)
        {
            Value rpy;

            if (data.payload && (data.size > 0))
            {
                panda::core::JsonParse((const char*)data.payload.get(), (const char*)data.payload.get() + data.size, rpy);      
            }

            handler(name, rpy);
        });

        return ret;
    }

    return PandaErrc::BadParameter;
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

ClientRequester::ClientRequester()
    :mImpl(new impl)
{
/** todo something */
}

ClientRequester::~ClientRequester()
{

}

/** try to find(or get) target service
    * if failed, then this function will be return error code immediately.
    */
//static 
ClientRequester* ClientRequester::getService(const std::string& srvName, ServiceWorkMode mode /*= ServiceWorkMode::kLocalOnly*/)
{
    IpcClientRequesterPtr pClient = MakeIpcClientRequesterPtr();

    if (mode == ServiceWorkMode::kLocalOnly)
    {
        PandaErrc ret = pClient->connect(srvName);

        if (PandaErrc::OK == ret)
        {
            ClientRequester* cr = new ClientRequester;
            cr->mImpl->mClient = pClient;
            return cr;
        }
    }
    else
    {
        /** todo something, implement it. */        
    }

    return nullptr;
}


/** try to find(or get) target service with async mode.
*  this function will be return immediately,
*  the 'handler' callback will be called
*  only already find(or get) service successfully.
*/
//static 
PandaErrc ClientRequester::getServiceAsync(const std::string& srvName, ClientFindServiceHandler handler, ServiceWorkMode mode /*= ServiceWorkMode::kLocalOnly*/)
{
    IpcClientRequesterPtr pClient = MakeIpcClientRequesterPtr();
    if (!handler) return PandaErrc::BadParameter;

    if (mode == ServiceWorkMode::kLocalOnly)
    {

        PandaErrc ret = pClient->connect(srvName);

        if (PandaErrc::OK == ret)
        {  
            ClientRequester* cr = new ClientRequester;
            cr->mImpl->mClient = pClient;
            handler(cr);
        }
        else
        {
            std::thread finder_thread([handler, pClient, srvName]() {
                LOGI("start find service: %s ...\n", srvName.c_str());
                while (1)
                {
                    PandaErrc res = pClient->connect(srvName);
                    if (PandaErrc::OK == res)
                    {
                        ClientRequester* cr = new ClientRequester;
                        cr->mImpl->mClient = pClient;
                        handler(cr);
                        break;
                    }

                    panda::core::SleepMs(1000);
                }
                });

            finder_thread.detach();
        }

        return PandaErrc::OK;
    }
    else
    {
        /** todo something, implement it. */
    }

    return PandaErrc::UnsupportedType; /** todo something */
}

//static 
bool ClientRequester::methodCall(const std::string& service, const std::string& method, const Value& param, Value& reply, ServiceWorkMode mode /*= ServiceWorkMode::kLocalOnly*/)
{
    ClientRequester* cr = ClientRequester::getService(service, mode);

    if (cr)
    {
        PandaErrc ret = cr->request(method, param, reply);
        return ret == PandaErrc::OK;
    }

    return false;
}


PandaErrc ClientRequester::request(const std::string& method, const Value& param, Value& reply, int32_t waitMs /*= COM_REQ_WAIT_INFINITE*/)
{
    return MyClientRequest(mImpl->mClient.get(), method, param, reply, waitMs);
}

PandaErrc ClientRequester::requestAsync(const std::string& method, const Value& param, ClientReplyHandler rpyHandler)
{
    return MyClientRequestAsync(mImpl->mClient.get(), method, param, rpyHandler);
}

PandaErrc ClientRequester::subscribeSignal(const std::string& sig, ClientSignalHandler sigHandler)
{
    if ((!sigHandler) || sig.empty()) return PandaErrc::BadParameter;

    return mImpl->mClient->subscribeSignal(sig, [sigHandler](const std::string& name, const IpcPacketData& data)
    {
        Value sigVal;

        if (data.payload && (data.size > 0))
        {
            panda::core::JsonParse((const char*)data.payload.get(), (const char*)data.payload.get() + data.size, sigVal);
        }
        sigHandler(name, sigVal);
    });
}

PandaErrc ClientRequester::unsubscribeSignal(const std::string& sig)
{
    return mImpl->mClient->unsubscribeSignal(sig);
}



} /** namespace com */
} /** namespace panda */


